/**
 * @author : River
 * @version : 1.1
 * @time : 2004.04.10
 */
package com.j3.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;

import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.commons.beanutils.BeanUtils;

import com.j3.string.StringUtils;

public class ConnectionManager {
    private static final String DEFAULT_VALIDATION_QUERY = "select 1";
    private static final ConnectionManager instance = new ConnectionManager();
    private final ConcurrentHashMap<String, DataSource> dataSourceTable = new ConcurrentHashMap<String, DataSource>();
    private String defaultDataSourceName = "gps";
    private boolean autoReconnect = true;
    private volatile boolean debug = false;

    private ConnectionManager() {
    }

    public static boolean testDataSource(Db db) throws SQLException, DataSourceUnRegisterException {
        Connection conn = getConn(db);
        return instance.testConnection(conn, db.getServerType().getValidationQuery());
    }

    private boolean testDataSource(String dataSourceName, String validationQuery) throws SQLException, DataSourceUnRegisterException {
        Connection conn = getConn(dataSourceName);
        boolean result = testConnection(conn, validationQuery);
        if (result) {
            output("Register datasource " + dataSourceName + " success!");
        } else {
            output("Register datasource " + dataSourceName + " failed!");
        }
        return result;
    }

    private boolean testConnection(Connection conn, String validationQuery) throws SQLException {
        boolean result = false;
        try {
            Statement stmt = conn.createStatement();
            ResultSet rset = null;
            if (StringUtils.isNotBlank(validationQuery)) {
                rset = stmt.executeQuery(validationQuery);
            } else {
                rset = stmt.executeQuery(DEFAULT_VALIDATION_QUERY);
            }
            if (rset.next()) {
                result = true;
            }
            rset.close();
            stmt.close();
        }
        finally {
            close(conn);
        }
        return result;
    }

    public static boolean registerDataSource(String dataSourceName, DataSource dataSource) throws NamingException, SQLException,
            DataSourceUnRegisterException {
        if (instance.dataSourceTable.containsKey(dataSourceName)) {
            return true;
        }
        instance.dataSourceTable.put(dataSourceName, dataSource);
        String validationQuery = null;
        try {
            validationQuery = BeanUtils.getProperty(dataSource, "validationQuery");
        }
        catch (Exception e) {
            // TODO
            e.printStackTrace();
        }
        return instance.testDataSource(dataSourceName, validationQuery);
    }

    public static Connection getConn() throws SQLException, DataSourceUnRegisterException {
        return getConn(instance.defaultDataSourceName);
    }

    public static Connection getConn(String dataSourceName) throws SQLException, DataSourceUnRegisterException {
        return instance.getConnection(dataSourceName);
    }

    private Connection getConnection(String dsName) throws SQLException, DataSourceUnRegisterException {
        String realDsName;
        if (dsName == null && defaultDataSourceName != null) {
            realDsName = defaultDataSourceName;
        } else {
            realDsName = dsName;
        }
        if (dataSourceTable == null || dataSourceTable.size() == 0) {
            throw new DataSourceUnRegisterException("No Datasource configed!");
        }
        if (realDsName == null || !dataSourceTable.containsKey(realDsName)) {
            throw new DataSourceUnRegisterException("Could not find DataSource '" + realDsName + "' !");
        }
        DataSource pool = dataSourceTable.get(realDsName);
        Connection conn = null;
        for (int i = 0; i < 2; i++) {
            try {
                conn = pool.getConnection();
                break;
            }
            catch (Exception e) {
                e.printStackTrace();
                if (!autoReconnect) {
                    break;
                }
            }
        }
        return conn;
    }

    public static void close(Connection conn) {
        if (conn == null)
            return;
        try {
            if (!conn.isClosed()) {
                conn.close();
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void output(String info) {
        if (!debug) {
            return;
        }
        System.out.println(info);
    }

    public static void clear() {
        Collection<DataSource> dataSources = instance.dataSourceTable.values();
        if (dataSources != null && !dataSources.isEmpty()) {
            instance.dataSourceTable.clear();
        }
    }

    public static String getDefaultDataSource() {
        return instance.defaultDataSourceName;
    }

    public static void setDefaultDataSourceName(String defaultDataSourceName) {
        instance.defaultDataSourceName = defaultDataSourceName;
    }

    public static void setDebug(boolean isDebug) {
        instance.debug = isDebug;
    }

    public static Connection getConn(Db db) throws DataSourceUnRegisterException, SQLException {
        if (db == null) {
            return getConn();
        }
        if (instance.dataSourceTable.containsKey(db.getServerName())) {
            return instance.getConnection(db.getServerName());
        }
        try {
            return db.getConnection();
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

}
